package me.zhengjie.domain;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.config.NetConfig;
import me.zhengjie.config.RegisterAddressConfig;
import me.zhengjie.dto.*;
import me.zhengjie.dto.sqlServer.PlcErrorInfo;
import me.zhengjie.dto.sqlServer.PlcGratingAlerts;
import me.zhengjie.dto.sqlServer.PlcStatusChange;
import me.zhengjie.modbusTCP.*;
import me.zhengjie.repository.PlcErrorInfoRepository;
import me.zhengjie.repository.PlcGratingAlertsRepository;
import me.zhengjie.repository.PlcStatusChangeRepository;
import me.zhengjie.util.LoginUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class PlcErrorService {

    private final PlcErrorInfoRepository plcErrorInfoRepository;
    private final LayUpTCP layUpTCP;
    private final NoTwoLayUpTCP noTwoLayUpTCP;
    private final NoThreeLayUpTcp noThreeLayUpTcp;
    private final UnbindingSplitTCP unbindingSplitTCP;
    private final LogisticsAreaTCP logisticsAreaTCP;
    private final UnbindingTCP unbindingTCP;
    private final NoTwoUnbindingTCP noTwoUnbindingTCP;
    private final NoThreeUnbindingTCP noThreeUnbindingTcp;
    /*private final Group1TCP group1TCP;
    private final Group2TCP group2TCP;
    private final Group3TCP group3TCP;
    private final LogisticsTCP logisticsTCP;
    private final TopCoverTCP topCoverTCP;
    private final Disassembly1TCP disassembly1TCP;
    private final Disassembly2TCP disassembly2TCP;
    private final Disassembly3TCP disassembly3TCP;*/
    private final SseService sseService;

    private static final Logger plcReportLog = LoggerFactory.getLogger("plcErrorInfo");
    private final NetConfig netConfig;
    private final PlcStatusChangeRepository plcStatusChangeRepository;
    private final PlcGratingAlertsRepository plcGratingAlertsRepository;

    private Map<String, Object> ipToTcpMap;

    @PostConstruct
    public void init() {
        ipToTcpMap = new HashMap<>();
        ipToTcpMap.put("192.168.4.10", layUpTCP);
        ipToTcpMap.put("192.168.4.30", noTwoLayUpTCP);
        ipToTcpMap.put("192.168.4.50", noThreeLayUpTcp);
        ipToTcpMap.put("192.168.4.70", unbindingSplitTCP);
        ipToTcpMap.put("192.168.4.80", logisticsAreaTCP);
        ipToTcpMap.put("192.168.4.20", unbindingTCP);
        ipToTcpMap.put("192.168.4.40", noTwoUnbindingTCP);
        ipToTcpMap.put("192.168.4.60", noThreeUnbindingTcp);
    }

    public void processExcelFile(MultipartFile file) {
        List<PlcErrorInfo> errorInfoList = new ArrayList<>();

        try (InputStream is = file.getInputStream();
             XSSFWorkbook workbook = new XSSFWorkbook(is)) {

            XSSFSheet sheet = workbook.getSheetAt(0);
            for (int i = 1; i <= sheet.getLastRowNum(); i++) {  // 从第二行开始
                XSSFRow row = sheet.getRow(i);
                if (row == null) continue;

                PlcErrorInfo info = new PlcErrorInfo();

                // 读取id（如果Excel中有id列）
                if (row.getCell(0) != null) {
                    info.setId((int) row.getCell(0).getNumericCellValue());
                }

                // 读取ip
                if (row.getCell(1) != null) {
                    info.setIp(getCellValueAsString(row.getCell(1)));
                }

                // 读取machCode
                if (row.getCell(2) != null) {
                    info.setMachCode(getCellValueAsString(row.getCell(2)));
                }

                // 读取address
                if (row.getCell(3) != null) {
                    info.setAddress(getCellValueAsString(row.getCell(3)));
                }

                // 读取digit
                if (row.getCell(4) != null) {
                    info.setDigit(getCellValueAsString(row.getCell(4)));
                }

                // 读取fullAddress
                if (row.getCell(5) != null) {
                    info.setFullAddress(getCellValueAsString(row.getCell(5)));
                }

                // 读取exCode
                if (row.getCell(6) != null) {
                    info.setExCode(getCellValueAsString(row.getCell(6)));
                }

                // 读取exDesc
                if (row.getCell(7) != null) {
                    info.setExDesc(getCellValueAsString(row.getCell(7)));
                }

                errorInfoList.add(info);
            }

            if (!errorInfoList.isEmpty()) {
                plcErrorInfoRepository.saveAll(errorInfoList);
            }
        } catch (Exception e) {
            plcReportLog.error("解析Excel文件时发生错误: {}", e.getMessage());
        }
    }

    /**
     * 根据单元格类型获取字符串值
     */
    private String getCellValueAsString(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    // 处理日期类型
                    return cell.getDateCellValue().toString();
                } else {
                    // 处理数值类型
                    double numericValue = cell.getNumericCellValue();
                    if (numericValue == Math.floor(numericValue)) {
                        // 如果是整数，去掉 .0
                        return String.valueOf((int) numericValue);
                    } else {
                        // 如果是小数，保留小数
                        return String.valueOf(numericValue);
                    }
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            default:
                return "";
        }
    }

    public ErrorInfoReport uploadErrorInfo(ErrorInfoRequest errorInfoRequest) {
        String url = "http://th.wusmes.com/mach/MLPinLamHuiLiu/Warning";
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String jsonBody = objectMapper.writeValueAsString(errorInfoRequest);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<String> requestEntity = new HttpEntity<>(jsonBody, headers);
            // 将响应体转为wcsResponse
            RestTemplate restTemplate = new RestTemplate();
            plcReportLog.error("机台准备上报报警信息{},上报时间:{}", requestEntity,LocalDateTime.now());
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
            String responseBody = responseEntity.getBody();
            ErrorInfoReport res = objectMapper.readValue(responseBody, ErrorInfoReport.class);
            if (res.getStatus_code() == 200) {
                plcReportLog.error("机台上报报警信息成功,上报时间:{}", LocalDateTime.now());
            } else {
                plcReportLog.error("机台上报报警信息出错,MES回抛错误原因:{}，响应体为{}", res.getMessage(),res);
            }
            return res;
        } catch (Exception e) {
            plcReportLog.error("机台上报报警信息出错,WMS错误原因:{}", e.getMessage(),e);
            return null;
        }
    }

    public StatusChangeReport uploadStatusChange(StatusChangeRequest StatusChangeRequest) {
        String url = "http://th.wusmes.com/mach/MLPinLamHuiLiu/StatusChange";
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String jsonBody = objectMapper.writeValueAsString(StatusChangeRequest);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<String> requestEntity = new HttpEntity<>(jsonBody, headers);
            // 将响应体转为wcsResponse
            plcReportLog.error("机台状态准备上传信息{},上报时间:{}", requestEntity,LocalDateTime.now());
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
            String responseBody = responseEntity.getBody();
            StatusChangeReport res = objectMapper.readValue(responseBody, StatusChangeReport.class);
            if (res.getStatus_code() == 200) {
                plcReportLog.error("机台状态上传成功,上报时间:{}", LocalDateTime.now());
            } else {
                plcReportLog.error("机台状态上传出错,MES回抛错误原因:{}，响应体为{}", res.getMessage(),res);
            }
            return res;
        } catch (Exception e) {
            plcReportLog.error("机台状态上传失败,WMS失败原因:{}", e.getMessage(),e);
            return null;
        }
    }

    public GratingAlertsReport uploadGratingAlerts(GratingAlertsRequest gratingAlertsRequest) {
        String url = "http://th.wusmes.com/mach/Grating/GratingWarning";
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            String jsonBody = objectMapper.writeValueAsString(gratingAlertsRequest);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<String> requestEntity = new HttpEntity<>(jsonBody, headers);
            plcReportLog.error("准备上报光栅报警信息{},上报时间:{}", requestEntity,LocalDateTime.now());
            // 将响应体转为wcsResponse
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
            String responseBody = responseEntity.getBody();
            GratingAlertsReport res = objectMapper.readValue(responseBody, GratingAlertsReport.class);
            if (res.getStatus_code() == 200) {
                plcReportLog.error("上报光栅报警信息成功,上报时间:{}", LocalDateTime.now());
            } else {
                plcReportLog.error("上报光栅报警信息出错,MES回抛错误原因:{}，响应体为{}", res.getMessage(),res);
            }
            return res;
        } catch (Exception e) {
            plcReportLog.error("上报光栅报警信息出错,WMS失败原因:{}", e.getMessage(),e);
            return null;
        }
    }

    public void checkInfoAndReport() {
        List<PlcErrorInfo> allInfos = plcErrorInfoRepository.findAll();
        // 按IP和Address分组去重
        Map<String, Set<String>> ipAddressMap = allInfos.stream()
                .collect(Collectors.groupingBy(PlcErrorInfo::getIp,
                        Collectors.mapping(PlcErrorInfo::getAddress, Collectors.toSet())));

        ipAddressMap.forEach((ip, addresses) -> {
            Object tcp = ipToTcpMap.get(ip);
            if (tcp == null) {
                plcReportLog.error("没有找到该{}", ip+"对应的tcpMaster");
                return;
            }

            addresses.forEach(address -> {
                try {
                    int value = readValueFromTcpClient(tcp, address);
                    processBinaryValue(ip, address, value);
                    plcReportLog.info("IP{}下，{}地址读取成功,值是{}", ip, address,value);
                } catch (Exception e) {
                    plcReportLog.error("IP{}下，{}地址读取失败,原因是{}",ip,address,e.getMessage(),e);
                }
            });
        });
    }

    public void checkStatusAndReport() {
        List<PlcStatusChange> plcStatusChanges = plcStatusChangeRepository.findAll();
        plcStatusChanges.forEach(plcStatusChange -> {
            Object tcp = ipToTcpMap.get(plcStatusChange.getIp());
            if (tcp != null) {
                Integer status = readValueFromTcpClient(tcp, plcStatusChange.getAddress());
                // 判断机台状态是否变更
                if (!status.equals(plcStatusChange.getStatus())){
                    /* 不相同
                      1.更新数据库
                      2.发送机台状态变更到MES
                      3.发送sse提示消息到
                     */
                    plcStatusChange.setStatus(status);
                    plcStatusChangeRepository.save(plcStatusChange);

                    StatusChangeReport report = uploadStatusChange(transformStatusChange(plcStatusChange));
                    if (report.getStatus_code() == 200){
                        sseService.sendStatusChangeMessage(report);
                    }
                }
            }
        });
    }

    public void checkGratingAlertsAndReport() {
        List<PlcGratingAlerts> allInfos = plcGratingAlertsRepository.findAll();

        // 按IP和Address分组去重
        Map<String, Set<String>> ipAddressMap = allInfos.stream()
                .collect(Collectors.groupingBy(PlcGratingAlerts::getIp,
                        Collectors.mapping(PlcGratingAlerts::getAddress, Collectors.toSet())));
        ipAddressMap.forEach((ip, addresses) -> {
            //plcReportLog.info("找到该{}", ip+"对应的tcpMaster");
            Object tcp = ipToTcpMap.get(ip);
            if (tcp == null) {
                plcReportLog.error("没有找到该{}", ip+"对应的tcpMaster");
                return;
            }

            addresses.forEach(address -> {
                try {
                    int value = readValueFromTcpClient(tcp, address);
                    if(value == 1){
                        PlcGratingAlerts plcGratingAlerts = plcGratingAlertsRepository
                                .findPlcErrorInfoByIpAndAddress(ip, address);
                        if (plcGratingAlerts != null) {
                            //log.info("光栅报警资料{},address{}", plcGratingAlerts, address);
                            handleGratingAlerts(plcGratingAlerts); // 处理查询到的记录
                        }
                        writeValueToTcpClient(tcp,Integer.parseInt(address));
                    }

                    plcReportLog.info("IP{}下，{}地址读取成功,值是{}", ip, address, value);
                } catch (Exception e) {
                    plcReportLog.error("IP{}下，{}地址读取失败,原因是{}",ip,address,e.getMessage(),e);
                }
            });
        });
    }

    private int readValueFromTcpClient(Object tcpClient, String address) {
        if (tcpClient instanceof LayUpTCP) {
            return ((LayUpTCP) tcpClient).readHoldingRegisters(Integer.parseInt(address));
        } else if (tcpClient instanceof NoTwoLayUpTCP) {
            return ((NoTwoLayUpTCP) tcpClient).readHoldingRegisters(Integer.parseInt(address));
        } else if (tcpClient instanceof NoThreeLayUpTcp) {
            return ((NoThreeLayUpTcp) tcpClient).readHoldingRegisters(Integer.parseInt(address));
        } else if (tcpClient instanceof UnbindingSplitTCP) {
            return ((UnbindingSplitTCP) tcpClient).readHoldingRegisters(Integer.parseInt(address));
        } else if (tcpClient instanceof LogisticsAreaTCP) {
            return ((LogisticsAreaTCP) tcpClient).readHoldingRegisters(Integer.parseInt(address));
        } else if (tcpClient instanceof UnbindingTCP) {
            return ((UnbindingTCP) tcpClient).readHoldingRegisters(Integer.parseInt(address));
        } else if (tcpClient instanceof NoTwoUnbindingTCP) {
            return ((NoTwoUnbindingTCP) tcpClient).readHoldingRegisters(Integer.parseInt(address));
        } else if (tcpClient instanceof NoThreeUnbindingTCP) {
            return ((NoThreeUnbindingTCP) tcpClient).readHoldingRegisters(Integer.parseInt(address));
        } else {
            throw new IllegalArgumentException("Unsupported TCP client type: " + tcpClient.getClass());
        }
    }

    private void writeValueToTcpClient(Object tcpClient, Integer address) {
        if (tcpClient instanceof LayUpTCP) {
            ((LayUpTCP) tcpClient).writeWithRetry(address, 0, 5);
        } else if (tcpClient instanceof NoTwoLayUpTCP) {
            ((NoTwoLayUpTCP) tcpClient).writeWithRetry(address, 0, 5);
        } else if (tcpClient instanceof NoThreeLayUpTcp) {
            ((NoThreeLayUpTcp) tcpClient).writeWithRetry(address, 0, 5);
        } else if (tcpClient instanceof UnbindingSplitTCP) {
            ((UnbindingSplitTCP) tcpClient).writeWithRetry(address, 0, 5);
        } else if (tcpClient instanceof LogisticsAreaTCP) {
            ((LogisticsAreaTCP) tcpClient).writeWithRetry(address, 0, 5);
        } else if (tcpClient instanceof UnbindingTCP) {
            ((UnbindingTCP) tcpClient).writeWithRetry(address, 0, 5);
        } else if (tcpClient instanceof NoTwoUnbindingTCP) {
            ((NoTwoUnbindingTCP) tcpClient).writeWithRetry(address, 0, 5);
        } else if (tcpClient instanceof NoThreeUnbindingTCP) {
            ((NoThreeUnbindingTCP) tcpClient).writeWithRetry(address, 0, 5);
        } else {
            throw new IllegalArgumentException("Unsupported TCP client type: " + tcpClient.getClass());
        }
    }

    private void processBinaryValue(String ip, String address, int value) {
        String binary = String.format("%16s", Integer.toBinaryString(value & 0xFFFF))
                .replace(' ', '0');
        StringBuilder sb = new StringBuilder(binary);
        binary = sb.reverse().toString();
        for (int i = 0; i < 16; i++) {
            if (binary.charAt(i) == '1') {
                String digit = String.valueOf(i + 1);
                PlcErrorInfo info = plcErrorInfoRepository
                        .findPlcErrorInfoByIpAndAddressAndDigit(ip, address, digit);
                if (info != null && info.getExDesc() != null && !info.getExDesc().isEmpty()) {
                    handleErrorInfo(info); // 处理查询到的记录
                }
            }
        }
    }

   /* private void processBinaryValueGrating(String ip, String address, int value) {
        String binary = String.format("%16s", Integer.toBinaryString(value & 0xFFFF))
                .replace(' ', '0');
        StringBuilder sb = new StringBuilder(binary);
        binary = sb.reverse().toString();
        plcReportLog.error("ip:{}的{}地址下转换为二进制十六位的字符串为:{}",ip,address,binary);
        for (int i = 0; i < 16; i++) {
            if (binary.charAt(i) == '1') {
                String digit = String.valueOf(i + 1);
                PlcGratingAlerts plcGratingAlerts = plcGratingAlertsRepository
                        .findPlcErrorInfoByIpAndAddressAndDigit(ip, address, digit);
                if (plcGratingAlerts != null) {
                    handleGratingAlerts(plcGratingAlerts); // 处理查询到的记录
                }
            }
        }
    }*/

    private void handleErrorInfo(PlcErrorInfo info) {
        // 根据需求处理错误信息，如记录日志或发送通知
        ErrorInfoReport errorInfoReport = uploadErrorInfo(transformErrorInfo(info));
        if (errorInfoReport != null && errorInfoReport.getStatus_code()==200) {
            // 用sse将错误信息推送至WMS
            sseService.sendErrorInfoMessage(errorInfoReport);
        }
    }

    private void handleGratingAlerts(PlcGratingAlerts plcGratingAlerts) {
        // 根据需求处理错误信息，如记录日志或发送通知
        GratingAlertsReport gratingAlertsReport = uploadGratingAlerts(transformGratingAlerts(plcGratingAlerts));
        if (gratingAlertsReport != null && gratingAlertsReport.getStatus_code()==200) {
            // 用sse将错误信息推送至WMS
            sseService.sendGratingAlertsMessage(gratingAlertsReport);
        }
    }

    private  ErrorInfoRequest transformErrorInfo(PlcErrorInfo info){
        ErrorInfoRequest errorInfoRequest = new ErrorInfoRequest();
        switch (info.getMachCode()){
            case "组合1":
                errorInfoRequest.setMach_code("B1ML-HLX001-00");
                break;
            case "组合2":
                errorInfoRequest.setMach_code("B1ML-HLX002-00");
                break;
            case "组合3":
                errorInfoRequest.setMach_code("B1ML-HLX003-00");
                break;
            default:
                errorInfoRequest.setMach_code("机台编码");
        }
        errorInfoRequest.setWo("");
        errorInfoRequest.setEx_grade("");
        errorInfoRequest.setEx_code(info.getExCode() == null ? "" : info.getExCode());
        errorInfoRequest.setEx_desc(info.getExDesc() == null ? "" : info.getExDesc());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        errorInfoRequest.setEx_time(formatter.format(LocalDateTime.now()));
        errorInfoRequest.setOp_name("admin");
        return errorInfoRequest;
    }

    private StatusChangeRequest transformStatusChange(PlcStatusChange plcStatusChange) {
        StatusChangeRequest statusChangeRequest = new StatusChangeRequest();
        switch (plcStatusChange.getMachCode()){
            case "组合1":
                statusChangeRequest.setMach_code("B1ML-HLX001-00");
                break;
            case "组合2":
                statusChangeRequest.setMach_code("B1ML-HLX002-00");
                break;
            case "组合3":
                statusChangeRequest.setMach_code("B1ML-HLX003-00");
                break;
            default:
                statusChangeRequest.setMach_code("机台编码");
        }
        statusChangeRequest.setWo("");
        Integer status = plcStatusChange.getStatus();
        if (status != null) {
            if (Objects.equals(plcStatusChange.getAddress(), "40051")){
                switch (plcStatusChange.getStatus()){
                    case 1:
                        statusChangeRequest.setStatus("手动");
                        break;
                    case 2:
                        statusChangeRequest.setStatus("自动");
                        break;
                    case 3:
                        statusChangeRequest.setStatus("急停");
                        break;
                    case 4:
                        statusChangeRequest.setStatus("保养");
                        break;
                }
            } else {
                statusChangeRequest.setStatus("异常中");
            }
        }
        statusChangeRequest.setOp_name("admin");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        statusChangeRequest.setC_time(formatter.format(LocalDateTime.now()));
        return statusChangeRequest;
    }

    private  GratingAlertsRequest transformGratingAlerts(PlcGratingAlerts plcGratingAlerts){
        GratingAlertsRequest gratingAlertsRequest = new GratingAlertsRequest();
        switch (plcGratingAlerts.getMachCode()){
            case "组合1":
                gratingAlertsRequest.setMach_code("B1ML-HLX001-00");
                break;
            case "组合2":
                gratingAlertsRequest.setMach_code("B1ML-HLX002-00");
                break;
            case "组合3":
                gratingAlertsRequest.setMach_code("B1ML-HLX003-00");
                break;
            default:
                gratingAlertsRequest.setMach_code("机台编码");
        }
        gratingAlertsRequest.setOp_name("admin");
        gratingAlertsRequest.setGrating_no(plcGratingAlerts.getGratingNo());
        gratingAlertsRequest.setGrating_name(plcGratingAlerts.getGratingName());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        gratingAlertsRequest.setEx_time(formatter.format(LocalDateTime.now()));
        gratingAlertsRequest.setEx_type(plcGratingAlerts.getIsAbnormal());
        return gratingAlertsRequest;
    }
}
